Skip to content

feat(desktop-ui): implement a desktop ui for appfaders#5

Merged
cheefbird merged 24 commits intomainfrom
feature/desktop-ui
Feb 2, 2026
Merged

feat(desktop-ui): implement a desktop ui for appfaders#5
cheefbird merged 24 commits intomainfrom
feature/desktop-ui

Conversation

@cheefbird
Copy link
Owner

@cheefbird cheefbird commented Jan 30, 2026

Context

Phase 3 of AppFaders - the desktop UI implementation. Transforms the app from a CLI prototype into a proper macOS menu bar application with a floating panel for volume control.

Changes Made

New Targets

  • AppFadersCore - shared library with TrackedApp, DriverBridge, AppAudioMonitor, HostProtocol

App Shell

  • AppFadersApp.swift + AppDelegate.swift - proper macOS app lifecycle with .accessory activation policy
  • MenuBarController.swift - NSStatusItem + NSPanel management, click-outside/escape dismiss

State Management

  • AppState.swift - @Observable state container for SwiftUI, per-app + master volume control
  • DeviceManager.swift - system volume control via CAAudioHardware

UI Components

  • VolumeSlider.swift - custom slider with filled track, large/small variants
  • MuteButton.swift - speaker/muted icon toggle
  • HeaderView.swift / FooterView.swift - panel chrome
  • MasterVolumeView.swift - system volume section
  • AppRowView.swift - per-app volume row with icon
  • PanelView.swift - root composition with scrollable app list

Theme

  • AppColors.swift - programmatic light/dark mode colors (asset catalog with SPM was giving me a headache, so I gave up for now)

Filtering

  • App list filtered by NSRunningApplication.activationPolicy == .regular (matches BackgroundMusic approach)

Tests

  • AppStateTests.swift - unit tests for AppVolumeState

Docs

  • future-integration-tests.md - recommendations for protocol-based mocking to enable AppState testing

Testing Notes

  • Run swift build then .build/debug/AppFaders to test
  • Click menu bar icon to show panel
  • Sliders control per-app volume (requires driver/helper for actual audio effect)
  • Master slider controls system volume immediately
  • Click outside or press Escape to dismiss
  • Right-click for context menu with Quit option
  • All 36 unit tests pass

Extract shared types and functionality from AppFaders into a dedicated
library target for reuse by desktop UI and future TUI implementations.
Move DriverBridge, DriverError, AppAudioMonitor, TrackedApp, and
HelperProtocol from AppFaders to AppFadersCore shared library. Update
Package.swift to add AppFadersCore dependency and update test imports.
Replace CLI entry point with proper macOS menu bar application architecture:
- AppFadersApp: @main entry using NSApplication.run() pattern for proper
  delegate lifecycle management
- AppDelegate: NSApplicationDelegate with .accessory activation policy,
  AudioOrchestrator integration, and graceful termination handling
- MenuBarController: NSStatusItem stub with SF Symbol icon (slider.vertical.3)

This establishes the foundation for the NSPanel-based volume control UI.
Add left-click panel toggle and right-click context menu to the menu bar
status item. The context menu provides "Open" and "Quit" options. Panel
show/hide methods are placeholders that log actions until the panel UI
is implemented in subsequent tasks.
- Configure NSPanel as floating, non-activating panel with borderless style
- Add PlaceholderPanelView with basic styling and dark/light mode support
- Wire panel show/hide to MenuBarController toggle methods
Position panel below menu bar status item with screen bounds checking.
Add event monitors to dismiss panel on click outside or Escape key press.
- Custom SwiftUI slider matching Pencil design specs.
- Large (300px) and small (200px) variants with DragGesture control.
- Asset catalog provides light/dark mode colors for all UI components.
- SF Symbol icons: speaker.wave.2.fill / speaker.slash.fill
- asset catalog colors for automatic light/dark mode adaptation.
- Panel chrome with title, settings icon placeholder, version label, and quit button.
- Uses asset catalog colors for light/dark mode.
- header row with "MASTER OUTPUT" label and percentage display.
- slider row with large VolumeSlider and MuteButton.
- added volume control row with icon, name/percentage display, small VolumeSlider, and MuteButton.
- added memberwise init for AppVolumeState to support previews.
- combines HeaderView, dividers, MasterVolumeView, app rows, and FooterView.
- added panel styling
- uses @bindable for AppState with async volume bindings.
- asset catalog wasn't working and I don't have patience to keep debugging right now.
…ix panel visibility

Asset catalogs aren't compiled by SPM's .process() directive, causing
runtime "No color named" errors. Replace with AppColors enum using
programmatic light/dark mode support.

- Remove Colors.xcassets and Resources directory
- Update all views to use AppColors instead of Color(bundle:)
- Set hidesOnDeactivate=false on NSPanel (required for menu bar apps)
- Wire PanelView to MenuBarController with AppState
- Remove unused PlaceholderPanelView
- added filtering for AppAudioMonitor with .regular to make list sensible.
- moved list population from async start() to init() so list pops faster.
@cheefbird cheefbird self-assigned this Feb 2, 2026
@cheefbird cheefbird marked this pull request as ready for review February 2, 2026 04:19
@cheefbird cheefbird merged commit 392027f into main Feb 2, 2026
7 checks passed
@cheefbird cheefbird deleted the feature/desktop-ui branch February 2, 2026 04:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant